#include "precomp.h"

#define DISABLE_FREETYPE	0

#define DEFAULT_CHAR_W		6
#define DEFAULT_CHAR_H		8

#define DEFAULT_FONT_W		5
#define DEFAULT_FONT_H		7

#if (!DISABLE_FREETYPE)
#include <LTY\LightType.h>
#endif

namespace Tmpl {

Font PixelFont;
Font TmplFont;

static inline void SetGlColor(Pixel color)
{
	glColor4ub((color&RMASK)>>RSHIFT, (color&GMASK)>>GSHIFT, (color&BMASK)>>BSHIFT, (color&AMASK)>>ASHIFT);
}

// ===========================================================
// Font pimpl declaration
// ===========================================================

class FontImp
{
public:
	FontImp() : myYOffset(-3){}
	~FontImp(){}

	void SetYoffset(int offset=-3) {myYOffset = offset;}
	
	virtual void Print(const char* string, int x, int y, Pixel color, Font::TextAlign halign, Font::TextAlign valign) const = 0;
	
	int PrintWrap(const char* string, int x, int y, int w, Pixel color, Font::TextAlign halign, char forceNewLine) const;
	int GetLineCount(const char* string, int w, char forceNewLine) const;

	virtual int GetWidth(char chr) const = 0;
	virtual int GetWidth(const char* string, int start, int lengh) const = 0;
	virtual int GetCharPos(const char* string, int x) const = 0;
	virtual int GetHeight() const = 0;
protected:
	int myYOffset;
};

// ===========================================================
// Fallback font class
// ===========================================================

static char sFont[95][DEFAULT_FONT_H][DEFAULT_FONT_W];
static int sTransl[256];

class FallbackFont : public FontImp
{
public:
	FallbackFont(){}
	~FallbackFont(){}

	int GetWidth(char chr) const							{return DEFAULT_CHAR_W;}
	int GetHeight() const									{return DEFAULT_CHAR_H;}
	int GetWidth(const char* str, int start, int len) const	{return len*DEFAULT_CHAR_W;}
	int GetCharPos(const char* str, int x) const			{return (x+DEFAULT_CHAR_W/2)/DEFAULT_CHAR_W;}
	void Print(const char* string, int x, int y, Pixel color, Font::TextAlign halign, Font::TextAlign valign) const;
};

static void PrintChar(char chr, int x, int y, Pixel col)
{
	const int charIdx = sTransl[(int)chr];
	const int w = DEFAULT_FONT_W, h = DEFAULT_FONT_H;

	const int depth = Draw::GetDepth();
	SetGlColor(col);
	glBegin(GL_POINTS);
	for(int ty=0; ty<h; ++ty)
	{
		char* font = &sFont[charIdx][ty][0];
		for(int tx=0; tx<w; ++tx) if(font[tx] == '@') glVertex3i(x+tx, y+ty, depth);
	}
	glEnd();
}

void FallbackFont::Print(const char* string, int x, int y, Pixel color, Font::TextAlign halign, Font::TextAlign valign) const
{
	int len = (int)strlen(string), start = 0;
	if(halign == Font::CENTER)	x -= GetWidth(string, 0, strlen(string))/2;
	if(halign == Font::RIGHT)	x -= GetWidth(string, 0, strlen(string));
	if(valign == Font::CENTER)	y -= GetHeight()/2;
	if(valign == Font::BOTTOM)	y -= GetHeight();

	for(int i=0; i<len; ++i)
	{
		const char chr = string[i];
		PrintChar(chr, x, y, color);
		x += DEFAULT_CHAR_W;
	}
}


// ===========================================================
// TrueType Font implementation
// ===========================================================

#if(!DISABLE_FREETYPE)

class FreeTypeFont : public FontImp
{
public:
	FreeTypeFont(){}
	~FreeTypeFont(){}

	bool LoadFromFile(const char* fontFile, int size);

	void Print(const char* string, int x, int y, Pixel color, Font::TextAlign halign, Font::TextAlign valign) const;
	int GetWidth(char chr) const;
	int GetWidth(const char* string, int start, int lengh) const;
	int GetCharPos(const char* string, int x) const;
	int GetHeight() const;

private:
	Image mFontImg;
	Lty::Font mFont;
};

bool FreeTypeFont::LoadFromFile(const char* fontFile, int size)
{
	bool loaded = mFont.LoadFromFile(fontFile, size);
	if(!loaded) return false;

	Lty::FontBitmap& bmp = mFont.GetBitmap();
	mFontImg.Create(bmp.pixelsRGBA, bmp.width, bmp.height, false);
	mFontImg.BuildTex();

	return true;
}

void FreeTypeFont::Print(const char* string, int x, int y, Pixel color, Font::TextAlign halign, Font::TextAlign valign) const
{
	if(string[0] == 0) return;

	y += myYOffset;
	if(halign == Font::CENTER)	x -= GetWidth(string, 0, strlen(string))/2;
	if(halign == Font::RIGHT)	x -= GetWidth(string, 0, strlen(string));
	if(valign == Font::CENTER)	y -= GetHeight()/2;
	if(valign == Font::BOTTOM)	y -= GetHeight();

	const ImageUvRect& uvs = mFontImg.GetUVs();
	const double uStart = (1.0 / (double)mFont.GetBitmap().width) * uvs.u0;
	const double vStart = (1.0 / (double)mFont.GetBitmap().height) * uvs.v0;
	const double uScale = (1.0 / (double)mFont.GetBitmap().width) * (uvs.u1 - uvs.u0);
	const double vScale = (1.0 / (double)mFont.GetBitmap().height) * (uvs.v1 - uvs.v0);
	
	//const sf::Font& font = mFont;
	const float CharSize =  (float)mFont.GetCharSize();

	// Prepare OpenGl for rendering
	Draw::SetBlendMode(Draw::ALPHA);
	
	SetGlColor(color);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D,  mFontImg.GetTexId());
	glBegin(GL_QUADS);

	// Initialize the rendering coordinates
	float X = 0;
	float Y = CharSize;

	// Draw one quad for each character
	const int strLen = (int)strlen(string);
	for(int i=0; i<strLen; ++i)
	{
		// Get the current character and its corresponding glyph
		const char				CurChar  = string[i];
		const Lty::Glyph&		CurGlyph = mFont.GetGlyph(string[i]);
		int						Advance  = CurGlyph.advance;
		const Lty::IntRect		Rect     = CurGlyph.rect;
		const Lty::IntRect		Coord    = CurGlyph.pixelCoords;

		// Handle special characters
		switch(CurChar)
		{
			case ' ':  X += Advance;		continue;
			case '\n': Y += Advance;		continue;
			case '\t': X += Advance * 4;	continue;
		}

		const int x1 = x+(int)X+Rect.left, y1 = y+(int)Y+Rect.top;
		const int x2 = x+(int)X+Rect.right, y2 = y+(int)Y+Rect.bottom;

		// Draw a textured quad for the current character
		const int depth = Draw::GetDepth();

		const double u0 = uStart + (double)Coord.left * uScale;
		const double u1 = uStart + (double)Coord.right * uScale;
		const double v0 = vStart + (double)Coord.top * vScale;
		const double v1 = vStart + (double)Coord.bottom * vScale;

		glTexCoord2d(u0, v0);	glVertex3i(x1, y1, depth);
		glTexCoord2d(u0, v1);	glVertex3i(x1, y2, depth);
		glTexCoord2d(u1, v1);	glVertex3i(x2, y2, depth);
		glTexCoord2d(u1, v0);	glVertex3i(x2, y1, depth);

		// Advance to the next character
		X += Advance;
	}

	glEnd();
	glDisable(GL_TEXTURE_2D);

	Draw::SetBlendMode();
}

int FreeTypeFont::GetWidth(char chr) const
{
	const Lty::Glyph&	CurGlyph = mFont.GetGlyph(chr);
	switch(chr) // Handle special characters
	{
		case '\v':
		case '\n':
		case '\t': return CurGlyph.advance*4;
	}
	return CurGlyph.advance;
}

int FreeTypeFont::GetWidth(const char* string, int start, int length) const
{
	const int maxlen = (int)strlen(string);
	if(start < 0) length += start, start = 0;
	if(start + length > maxlen) length = Max(0, maxlen - start);
	
	// Initial values
	float Width = 0, MaxWidth = 0;

	// Go through each character
	for(int i=start; i<length; ++i)
	{
		// Get the current character and its corresponding glyph
		const char				CurChar  = string[i];
		const Lty::Glyph&		CurGlyph = mFont.GetGlyph(string[i]);
		int						Advance  = CurGlyph.advance;
		const Lty::IntRect		Rect     = CurGlyph.rect;

		switch(CurChar) // Handle special characters
		{
			case L'\v':
			case L'\n': continue;
			case L'\t': Advance *= 4;
		}
		// Advance to the next character
		Width += Advance;
	}
	MaxWidth = Max(MaxWidth, Width);
	return (int)MaxWidth;
}

int FreeTypeFont::GetCharPos(const char* string, int x) const
{
	// Initial values
	const float X = (float)x;
	float Width = 0;

	// Go through each character
	const int len = (int)strlen(string);
	for(int i=0; i<len; ++i)
	{
		// Get the current character and its corresponding glyph
		const char				CurChar  = string[i];
		const Lty::Glyph&		CurGlyph = mFont.GetGlyph(string[i]);
		int						Advance  = CurGlyph.advance;
		const Lty::IntRect		Rect     = CurGlyph.rect;

		switch(CurChar) // Handle special characters
		{
			case L'\v':
			case L'\n': continue;
			case L'\t': Advance *= 4;
		}
		// Advance to the next character
		Width += Advance*0.5f;
		if(Width > X) return i;
		Width += Advance*0.5f;
	}
	return len;
}

int FreeTypeFont::GetHeight() const
{
	return (int)mFont.GetCharSize();
}

#endif /* !DISABLE_FREETYPE */

// ===========================================================
// Generic Font class functionality
// ===========================================================

int FontImp::GetLineCount(const char* string, int w, char forceNewLine) const
{
	// Easy way out, string fits on a single line
	if(forceNewLine == 0 && GetWidth(string, 0, strlen(string)) <= w) return 1;

	const int strLen = strlen(string);
	int wordLen=0, lineLen=0, lineCount=0;

	// Looks like we'll have to spread across multiple lines
	for(int i=0; i<strLen && lineCount<64; ++i)
	{
		const char chr = string[i];
		const int chrLen = GetWidth(string[i]);
		const bool makeNewLine = (chr == forceNewLine) || (wordLen+lineLen+chrLen > w);

		// The line can be cut at the end of every completed word (at spaces), or at a forceNewLine
		if(chr == ' ' || chr == forceNewLine)
		{
			lineLen += wordLen;
			wordLen = 0;
			if(chr == ' ')
			{
				lineLen += chrLen;
				continue;
			}
		}

		// New line occured, print the current line and advance to the next
		if(makeNewLine)
		{
			// If we haven't completed the first word of the line yet, 
			// we'll print it anyway and split it across multiple lines
			if(lineLen==0) wordLen = 0;
		
			// Advancing to a next line
			lineLen = 0;
			++lineCount;
		}

		// Add new character to next word
		if(chr != forceNewLine)	wordLen += chrLen;
	}
	// Finally, output anything that we haven't printed yet
	if(wordLen>0 || lineLen>0) ++lineCount;

	return lineCount;
}

int FontImp::PrintWrap(const char* string, int x, int y, int w, Pixel color, Font::TextAlign halign, char forceNewLine) const
{
	if(halign == Font::RIGHT) x += w;
	if(halign == Font::CENTER) x += w/2;

	// Easy way out, string fits on a single line
	if(forceNewLine == 0 && GetWidth(string, 0, strlen(string)) <= w)
	{
		Print(string, x, y, color, halign, Font::TOP);
		return 1;
	}

	std::string lineStr, wordStr;
	const int strLen = strlen(string), fontH = GetHeight();
	int wordLen=0, lineLen=0, lineCount=0;

	// Looks like we'll have to spread across multiple lines
	for(int i=0; i<strLen && lineCount<64; ++i)
	{
		const char chr = string[i];
		const int chrLen = GetWidth(string[i]);
		const bool makeNewLine = (chr == forceNewLine) || (wordLen+lineLen+chrLen > w);

		// The line can be cut at the end of every completed word (at spaces), or at a forceNewLine
		if(chr == ' ' || chr == forceNewLine)
		{
			lineStr.append(wordStr);
			lineLen += wordLen;
			wordStr.clear();
			wordLen = 0;
			if(chr == ' ')
			{
				lineStr.push_back(chr);
				lineLen += chrLen;
				continue;
			}
		}

		// New line occured, print the current line and advance to the next
		if(makeNewLine)
		{
			// If we haven't completed the first word of the line yet, 
			// we'll print it anyway and split it across multiple lines
			if(lineStr.empty())
			{
				lineStr = wordStr;
				wordStr.clear();
				wordLen = 0;
			}

			// If we're not left aligned, we need to remove trailing whitespace
			// because it will cause the text to offset to the left
			if(halign != Font::LEFT && !lineStr.empty())
			{
				if(*(lineStr.end()-1)==' ') lineStr.erase(lineStr.end()-1);
			}

			// Printing the current line...
			Print(lineStr.c_str(), x, y, color, halign, Font::TOP);
			lineStr.clear();
			lineLen = 0;
			
			// Advancing to a next line
			y += fontH;
			++lineCount;
		}

		// Add new character to next word
		if(chr != forceNewLine)
		{
			wordStr.push_back(chr);
			wordLen += chrLen;
		}
	}
	// Finally, output anything that we haven't printed yet
	if(wordLen>0 || lineLen>0)
	{
		lineStr.append(wordStr);
		Print(lineStr.c_str(), x, y, color, halign, Font::TOP);
		++lineCount;
	}
	return lineCount;
}

// ===========================================================
// Font wrapper API
// ===========================================================

Font::Font()
{
	mFont = new FallbackFont();
}

Font::Font(const char* fontFile, int size) : mFont(0)
{
	LoadFromFile(fontFile, size);
}

Font::~Font()
{
	delete mFont;
}

static void FallbackFontInitCharset();
void Font::InitDefault()
{
	FallbackFontInitCharset();
	TmplFont.LoadFromFile("fonts/segoeui.ttf", 11);
}

void Font::LoadFromFile(const char* fontFile, int size)
{
	if(mFont) delete mFont;
#if(!DISABLE_FREETYPE)
	FreeTypeFont* font = new FreeTypeFont();
	if(!font->LoadFromFile(fontFile, size))
	{
		delete font;
		mFont = new FallbackFont();
	}
	else mFont = font;
#else
	mFont = new FallbackFont();
#endif
}

void Font::SetYoffset(int offset)
{
	mFont->SetYoffset(offset);
}

void Font::Print(const char* string, int x, int y, Pixel color, Font::TextAlign halign, Font::TextAlign valign) const
{
	return mFont->Print(string, x, y, color, halign, valign);
}

int Font::PrintWrap(const char* string, int x, int y, int w, Pixel color, Font::TextAlign halign, char forceNewLine) const
{
	return mFont->PrintWrap(string, x, y, w, color, halign, forceNewLine);
}

int Font::GetLineCount(const char* string, int w, char forceNewLine) const
{
	return mFont->GetLineCount(string, w, forceNewLine);
}

int Font::GetWidth(char chr) const
{
	return mFont->GetWidth(chr);
}

int Font::GetWidth(const char* string, int start) const
{
	return GetWidth(string, start, strlen(string) - start);
}

int Font::GetWidth(const char* string, int start, int length) const
{
	return mFont->GetWidth(string, start, length);
}

int Font::GetCharPos(const char* string, int x) const
{
	return mFont->GetCharPos(string, x);
}

int Font::GetHeight() const
{
	return mFont->GetHeight();
}

// ===========================================================
// Fallback Font character set
// ===========================================================

static void _SetChar5(int chr, int line, char* c1, char* c2, char* c3, char* c4, char* c5)
{
	memcpy(sFont[chr+0][line], c1, 5);
	memcpy(sFont[chr+1][line], c2, 5);
	memcpy(sFont[chr+2][line], c3, 5);
	memcpy(sFont[chr+3][line], c4, 5);
	memcpy(sFont[chr+4][line], c5, 5);
}

static void FallbackFontInitCharset()
{
	static bool sTmplFontInitialized = false;
	if(sTmplFontInitialized) return;
	sTmplFontInitialized = true;

	_SetChar5( 0, 0, " @@@ ", "@@@@ ", " @@@@", "@@@@ ", "@@@@@");
	_SetChar5( 0, 1, "@   @", "@   @", "@    ", "@   @", "@    ");
	_SetChar5( 0, 2, "@   @", "@   @", "@    ", "@   @", "@    ");
	_SetChar5( 0, 3, "@@@@@", "@@@@ ", "@    ", "@   @", "@@@@ ");
	_SetChar5( 0, 4, "@   @", "@   @", "@    ", "@   @", "@    ");
	_SetChar5( 0, 5, "@   @", "@   @", "@    ", "@   @", "@    ");
	_SetChar5( 0, 6, "@   @", "@@@@ ", " @@@@", "@@@@ ", "@@@@@");

	_SetChar5( 5, 0, "@@@@@", " @@@@", "@   @", " @@@ ", " @@@ ");
	_SetChar5( 5, 1, "@    ", "@    ", "@   @", "  @  ", "   @ ");
	_SetChar5( 5, 2, "@    ", "@    ", "@   @", "  @  ", "   @ ");
	_SetChar5( 5, 3, "@@@  ", "@ @@@", "@@@@@", "  @  ", "   @ ");
	_SetChar5( 5, 4, "@    ", "@   @", "@   @", "  @  ", "   @ ");
	_SetChar5( 5, 5, "@    ", "@   @", "@   @", "  @  ", "   @ ");
	_SetChar5( 5, 6, "@    ", " @@@ ", "@   @", " @@@ ", "@@@  ");

	_SetChar5(10, 0, "@   @", "@    ", "@   @", "@   @", " @@@ ");
	_SetChar5(10, 1, "@  @ ", "@    ", "@@ @@", "@@  @", "@   @");
	_SetChar5(10, 2, "@ @  ", "@    ", "@ @ @", "@ @ @", "@   @");
	_SetChar5(10, 3, "@@   ", "@    ", "@   @", "@  @@", "@   @");
	_SetChar5(10, 4, "@ @  ", "@    ", "@   @", "@   @", "@   @");
	_SetChar5(10, 5, "@  @ ", "@    ", "@   @", "@   @", "@   @");
	_SetChar5(10, 6, "@   @", "@@@@@", "@   @", "@   @", " @@@ ");

	_SetChar5(15, 0, "@@@@ ", " @@@ ", "@@@@ ", " @@@@", "@@@@@");
	_SetChar5(15, 1, "@   @", "@   @", "@   @", "@    ", "  @  ");
	_SetChar5(15, 2, "@   @", "@   @", "@   @", "@    ", "  @  ");
	_SetChar5(15, 3, "@@@@ ", "@   @", "@@@@ ", " @@@ ", "  @  ");
	_SetChar5(15, 4, "@    ", "@ @ @", "@  @ ", "    @", "  @  ");
	_SetChar5(15, 5, "@    ", "@  @@", "@   @", "    @", "  @  ");
	_SetChar5(15, 6, "@    ", " @@@@", "@   @", "@@@@ ", "  @  ");
	
	_SetChar5(20, 0, "@   @", "@   @", "@   @", "@   @", "@   @");
	_SetChar5(20, 1, "@   @", "@   @", "@   @", "@   @", "@   @");
	_SetChar5(20, 2, "@   @", "@   @", "@ @ @", " @ @ ", "@   @");
	_SetChar5(20, 3, "@   @", " @ @ ", "@ @ @", "  @  ", " @@@@");
	_SetChar5(20, 4, "@   @", " @ @ ", " @ @ ", " @ @ ", "    @");
	_SetChar5(20, 5, "@   @", " @ @ ", " @ @ ", "@   @", "    @");
	_SetChar5(20, 6, " @@@@", "  @  ", " @ @ ", "@   @", " @@@ ");
			  
	_SetChar5(25, 0, "@@@@@", "     ", "@    ", "     ", "    @");
	_SetChar5(25, 1, "    @", "     ", "@    ", "     ", "    @");
	_SetChar5(25, 2, "   @ ", " @@@ ", "@@@@ ", " @@@ ", " @@@@");
	_SetChar5(25, 3, "  @  ", "    @", "@   @", "@    ", "@   @");
	_SetChar5(25, 4, " @   ", " @@@@", "@   @", "@    ", "@   @");
	_SetChar5(25, 5, "@    ", "@   @", "@   @", "@   @", "@   @");
	_SetChar5(25, 6, "@@@@@", " @@@@", "@@@@ ", " @@@ ", " @@@@");

	_SetChar5(30, 0, "     ", "  @@@", "     ", "@    ", "  @  ");
	_SetChar5(30, 1, "     ", " @   ", "     ", "@    ", "     ");
	_SetChar5(30, 2, " @@@ ", " @   ", " @@@ ", "@ @@ ", " @@  ");
	_SetChar5(30, 3, "@   @", "@@@@ ", "@    ", "@@  @", "  @  ");
	_SetChar5(30, 4, "@@@@@", " @   ", "@  @@", "@   @", "  @  ");
	_SetChar5(30, 5, "@    ", " @   ", "@   @", "@   @", "  @  ");
	_SetChar5(30, 6, " @@@ ", " @   ", " @@@ ", "@   @", " @@@ ");
			  
	_SetChar5(35, 0, "   @ ", "@    ", " @@  ", "     ", "     ");
	_SetChar5(35, 1, "     ", "@    ", "  @  ", "     ", "     ");
	_SetChar5(35, 2, "  @@ ", "@  @ ", "  @  ", "@@ @ ", " @@@ ");
	_SetChar5(35, 3, "   @ ", "@ @  ", "  @  ", "@ @ @", "@   @");
	_SetChar5(35, 4, "   @ ", "@@   ", "  @  ", "@ @ @", "@   @");
	_SetChar5(35, 5, "@  @ ", "@ @  ", "  @  ", "@   @", "@   @");
	_SetChar5(35, 6, " @@  ", "@  @ ", " @@@ ", "@   @", "@   @");
			  
	_SetChar5(40, 0, "     ", "     ", "     ", "     ", "     ");
	_SetChar5(40, 1, "     ", "     ", "     ", "     ", "     ");
	_SetChar5(40, 2, " @@@ ", "@@@@ ", " @@@ ", "@ @@@", " @@@@");
	_SetChar5(40, 3, "@   @", "@   @", "@   @", "@@   ", "@    ");
	_SetChar5(40, 4, "@   @", "@@@@ ", "@ @ @", "@    ", " @@@ ");
	_SetChar5(40, 5, "@   @", "@    ", "@  @@", "@    ", "    @");
	_SetChar5(40, 6, " @@@ ", "@    ", " @@ @", "@    ", "@@@@ ");
			  
	_SetChar5(45, 0, " @   ", "     ", "     ", "     ", "     ");
	_SetChar5(45, 1, " @   ", "     ", "     ", "     ", "     ");
	_SetChar5(45, 2, "@@@@@", "@   @", "@   @", "@   @", "@   @");
	_SetChar5(45, 3, " @   ", "@   @", "@   @", "@ @ @", " @ @ ");
	_SetChar5(45, 4, " @   ", "@   @", " @ @ ", "@ @ @", "  @  ");
	_SetChar5(45, 5, " @   ", "@  @@", " @ @ ", "@ @ @", " @ @ ");
	_SetChar5(45, 6, "  @@@", " @@ @", "  @  ", " @ @ ", "@   @");
			  
	_SetChar5(50, 0, "     ", "     ", " @   ", "  @  ", " @@@ ");
	_SetChar5(50, 1, "     ", "     ", "  @  ", " @@  ", "@   @");
	_SetChar5(50, 2, "@   @", "@@@@@", "     ", "@ @  ", "    @");
	_SetChar5(50, 3, "@   @", "   @ ", "     ", "  @  ", "   @ ");
	_SetChar5(50, 4, " @@@@", "  @  ", "     ", "  @  ", "  @  ");
	_SetChar5(50, 5, "    @", " @   ", "     ", "  @  ", " @   ");
	_SetChar5(50, 6, " @@@ ", "@@@@@", "     ", "@@@@@", "@@@@@");
			  
	_SetChar5(55, 0, "@@@@ ", "@    ", "@@@@@", " @@@@", "@@@@@");
	_SetChar5(55, 1, "    @", "@  @ ", "@    ", "@    ", "    @");
	_SetChar5(55, 2, "    @", "@  @ ", "@    ", "@    ", "    @");
	_SetChar5(55, 3, " @@@ ", "@@@@@", "@@@@ ", "@@@@ ", "   @ ");
	_SetChar5(55, 4, "    @", "   @ ", "    @", "@   @", "  @  ");
	_SetChar5(55, 5, "    @", "   @ ", "    @", "@   @", "  @  ");
	_SetChar5(55, 6, "@@@@ ", "   @ ", "@@@@ ", " @@@ ", "  @  ");
			  
	_SetChar5(60, 0, " @@@ ", " @@@ ", " @@@ ", "     ", "     ");
	_SetChar5(60, 1, "@   @", "@   @", "@   @", "     ", "     ");
	_SetChar5(60, 2, "@   @", "@   @", "@  @@", "     ", "@@@@@");
	_SetChar5(60, 3, " @@@ ", " @@@@", "@ @ @", "@@@@@", "     ");
	_SetChar5(60, 4, "@   @", "    @", "@@  @", "     ", "@@@@@");
	_SetChar5(60, 5, "@   @", "    @", "@   @", "     ", "     ");
	_SetChar5(60, 6, " @@@ ", " @@@ ", " @@@ ", "     ", "     ");
			  
	_SetChar5(65, 0, "     ", "  @  ", " @@@ ", " @ @ ", " @@@@");
	_SetChar5(65, 1, "     ", "  @  ", "@   @", " @ @ ", "@ @  ");
	_SetChar5(65, 2, " @@  ", "  @  ", "@ @@@", "@@@@@", "@ @  ");
	_SetChar5(65, 3, "@ @ @", "  @  ", "@ @ @", " @ @ ", " @@@ ");
	_SetChar5(65, 4, "  @@ ", "  @  ", "@ @@ ", "@@@@@", "  @ @");
	_SetChar5(65, 5, "     ", "     ", "@    ", " @ @ ", "  @ @");
	_SetChar5(65, 6, "     ", "  @  ", " @@@ ", " @ @ ", "@@@@ ");
			  
	_SetChar5(70, 0, "     ", "  @  ", " @   ", "     ", "   @ ");
	_SetChar5(70, 1, "@   @", " @ @ ", "@ @  ", "  @  ", "  @  ");
	_SetChar5(70, 2, "   @ ", "     ", "@ @ @", "  @  ", "  @  ");
	_SetChar5(70, 3, "  @  ", "     ", " @@ @", "@@@@@", "  @  ");
	_SetChar5(70, 4, " @   ", "     ", "@  @ ", " @@@ ", "  @  ");
	_SetChar5(70, 5, "@   @", "     ", "@  @@", "@   @", "  @  ");
	_SetChar5(70, 6, "     ", "     ", " @@ @", "     ", "   @ ");
			  
	_SetChar5(75, 0, "  @  ", "     ", "     ", "  @@ ", " @@  ");
	_SetChar5(75, 1, "   @ ", "     ", "  @  ", "  @  ", "  @  ");
	_SetChar5(75, 2, "   @ ", "     ", "  @  ", "  @  ", "  @  ");
	_SetChar5(75, 3, "   @ ", "     ", "@@@@@", "  @  ", "  @  ");
	_SetChar5(75, 4, "   @ ", "     ", "  @  ", "  @  ", "  @  ");
	_SetChar5(75, 5, "   @ ", "     ", "  @  ", "  @  ", "  @  ");
	_SetChar5(75, 6, "  @  ", "@@@@@", "     ", "  @@ ", " @@  ");
			  
	_SetChar5(80, 0, "  @@ ", " @@  ", "     ", "  @  ", "     ");
	_SetChar5(80, 1, "  @  ", "  @  ", "     ", "  @  ", " @   ");
	_SetChar5(80, 2, "  @  ", "  @  ", "  @  ", "     ", " @   ");
	_SetChar5(80, 3, " @   ", "   @ ", "     ", "     ", "  @  ");
	_SetChar5(80, 4, "  @  ", "  @  ", "     ", "     ", "  @  ");
	_SetChar5(80, 5, "  @  ", "  @  ", "  @  ", "     ", "   @ ");
	_SetChar5(80, 6, "  @@ ", " @@  ", " @   ", "     ", "   @ ");
			  
	_SetChar5(85, 0, "     ", " @ @ ", "  @  ", "     ", "     ");
	_SetChar5(85, 1, "     ", " @ @ ", "  @  ", "     ", "     ");
	_SetChar5(85, 2, "  @  ", "     ", "  @  ", "     ", "     ");
	_SetChar5(85, 3, "     ", "     ", "  @  ", "     ", "     ");
	_SetChar5(85, 4, "     ", "     ", "  @  ", "     ", "     ");
	_SetChar5(85, 5, "  @  ", "     ", "  @  ", "  @  ", "     ");
	_SetChar5(85, 6, "     ", "     ", "  @  ", " @   ", "  @  ");
			  
	_SetChar5(90, 0, "     ", "     ", "     ", " @@@ ", "     ");
	_SetChar5(90, 1, "   @ ", "     ", "     ", "@   @", "     ");
	_SetChar5(90, 2, "   @ ", "   @ ", " @   ", "    @", "     ");
	_SetChar5(90, 3, "  @  ", "  @  ", "  @  ", "   @ ", "     ");
	_SetChar5(90, 4, "  @  ", " @   ", "   @ ", "  @  ", "     ");
	_SetChar5(90, 5, " @   ", "  @  ", "  @  ", "     ", "     ");
	_SetChar5(90, 6, " @   ", "   @ ", " @   ", "  @  ", "     ");

	const char c[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
					 "`1234567890-=~!@#$%^&*()_+[]{};'\\:\"|,./<>? ";

	for(int i=0; i<256; i++) sTransl[i] = 94;
	for(int i=0; i<95; i++) sTransl[c[i]] = i;
}

}; // namespace Tmpl
